﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

using Newtonsoft.Json;

namespace Webprox
{
    /// <summary>
    /// Proxy接口的生成类，只允许在 Debug模式下面使用
    /// </summary>
    public class ProxyParser
    {
        #region 私有变量

        /// <summary>
        /// 方法信息的元数据
        /// </summary>
        class metainfos
        {
            public string MethodName { get; set; }
            public string ReturnType { get; set; }
            public Dictionary<string, int> Support { get; set; }
            public bool IsValidate { get; set; }
            public Dictionary<string, string> Arguments { get; set; }
        }

        /// <summary>
        /// 所有的模块
        /// </summary>
        public Type[] _moduleClasses = null;

        #endregion

        #region 辅助的处理方法

        /// <summary>
        /// 从某个module里面获取所有带有标记的方法，如果没有找到方法则输出null
        /// </summary>
        /// <param name="module">模块数据</param>
        /// <returns></returns>
        private metainfos[] parseMethodsInModule(Type module)
        {
            // 获取class里面所有的方法，不包含构造函数
            var methods = module.GetMethods(BindingFlags.Public | BindingFlags.Instance);
            if (methods == null || methods.Length == 0)
                return null;

            // 查看是否含有webproxy属性，有的话算一个
            List<metainfos> result = new List<metainfos>();
            foreach (var mt in methods)
            {
                // 如果是继承下来的方法就忽略,用declaretype判断
                if (mt.DeclaringType != module)
                    continue;

                // 获取attribute
                var attrs = mt.GetCustomAttributes(typeof(WebproxyAttribute), false);
                if (attrs == null)
                    continue;
                else
                {
                    // 获取各样metainfos的信息
                    WebproxyAttribute wa = (WebproxyAttribute)attrs[0];

                    metainfos mi = new metainfos();
                    mi.MethodName = mt.Name;
                    mi.ReturnType = this.getShortTypename(mt.ReturnType);
                    mi.IsValidate = wa.UseValidate;

                    mi.Support = new Dictionary<string, int>();
                    mi.Support["Desktop"] = wa.ForDesktop ? 1 : 0;
                    mi.Support["Mobile"] = wa.ForMobile ? 1 : 0;
                    mi.Support["Web"] = wa.ForWeb ? 1 : 0;

                    mi.Arguments = new Dictionary<string, string>();
                    foreach (var args in mt.GetParameters())
                    {
                        mi.Arguments[args.Name] = this.getShortTypename(args.ParameterType);
                    }

                    // 都获取成功就加入结果中
                    result.Add(mi);
                }
            }

            return result == null ? null : result.ToArray();
        }

        /// <summary>
        /// 将类型转换为通用的短字符标识。
        /// </summary>
        /// <param name="t">类型</param>
        /// <returns></returns>
        private string getShortTypename(Type t)
        {
            string n = "";

            switch (t.Name)
            {
                case "Object":
                    n = "_";
                    break;
                case "Int32":
                    n = "i";
                    break;
                case "Int64":
                    n = "I";
                    break;
                case "String":
                    n = "s";
                    break;
                case "Boolean":
                    n = "b";
                    break;
                case "Single":
                    n = "f";
                    break;
                case "Double":
                    n = "F";
                    break;
                case "Int32[]":
                    n = "ai";
                    break;
                case "Int64[]":
                    n = "nI";
                    break;
                case "String[]":
                    n = "as";
                    break;
                case "Boolean[]":
                    n = "ab";
                    break;
                case "Single[]":
                    n = "af";
                    break;
                case "Double[]":
                    n = "aF";
                    break;
                case "Hashtable":
                    n = "h";
                    break;
                case "List`1":          // 泛型参数后面都有一个数字表示参数的数量
                    n = "l";
                    n += getShortTypename(t.GetGenericArguments()[0]);
                    break;
                case "Dictionary`2":
                    n = "d";
                    n += getShortTypename(t.GetGenericArguments()[0]);
                    n += getShortTypename(t.GetGenericArguments()[1]);
                    break;
                default:
                    return string.Empty;
            }

            return n;
        }

        #endregion

        #region 构造函数和共有方法

        /// <summary>
        /// 构造函数，获取命名空间里面所有的类
        /// </summary>
        /// <param name="moduleNamespace">模块所在的命名空间</param>
        public ProxyParser(string moduleNamespace)
        {
            _moduleClasses = Assembly.Load(moduleNamespace).GetTypes();
            if (_moduleClasses == null)
            {
                throw new Exception("找不到任何模块信息");
            }
        }


        /// <summary>
        /// 遍历接口信息，并且生成元数据文件
        /// </summary>
        /// <returns></returns>
        public string ParseProxyMethods()
        {
            // 保存接口生成出来的元数据
            Dictionary<string, metainfos[]> MODULES = new Dictionary<string, metainfos[]>();

            // 遍历，寻找接口
            foreach (var c in _moduleClasses)
            {
                var meta = parseMethodsInModule(c);

                // 如果存在有接口信息，则保存的MODULES里面
                if (meta != null)
                {
                    MODULES[c.Name] = meta;
                }
            }

            // 有接口数据则返回。没有则返回空
            if (MODULES.Count > 0)
            {
                return JsonConvert.SerializeObject(MODULES, Formatting.Indented);
            }
            else
                return string.Empty;
        }

        #endregion
    }
}
